/* Layout manager for CairoUI widgets */

#include <cairo.h>

#include "mcp.h"
#include "configfile.h"

#include "ui_cairo.h"
#include "uic_widget.h"

namespace uic
{

/*
 * WARNING: Unrefactored code within.  Be wary.
 */

//    Layout declarations                                                 >fold>

struct dimensions
    {
    double x1, y1,
           x2, y2;
    double width, height;
    };

// Magic values for positions and sizes

const int INHERIT         = -1,
          LEFT_OF         = -2,
          RIGHT_OF        = -3,
          CENTER_OF       = -4,
          CENTER          = -9,
          CENTER_TO_RIGHT = -10,
          CENTER_TO_LEFT  = -11;
const int //INHERIT      = -1,
          ABOVE        = -2,
          BELOW        = -3,
//        CENTER_OF    = -4,
//        CENTER       = -9,
          CENTER_ABOVE = -10,
          CENTER_BELOW = -11;
const int // INHERIT     = -1,
          FULL         = -2,  // rarely useful if x is not 0
          EXPAND_RIGHT = -3,  // x must be a value or RIGHT_OF
          EXPAND_LEFT  = -4,  // x must be a value or LEFT_OF
          EXPAND       = -5;  // x must be a value or CENTER_OF
const int //INHERIT       = -1,
          //FULL          = -2,  // rarely useful if y is not 0
          EXPAND_UP     = -3,
          EXPAND_DOWN   = -4;
          //EXPAND        = -5;

class Layout;
class LayoutProcessor;

class Position
    {
  public:
    Position () : stopped(false), playing(false), disconnected(false),
                  x_reverse(false), y_reverse(false) {}
//                  inherit_x(false), inherit_y(false) //{}
    bool stopped,       //// only
         playing,       ///  shown
         disconnected;  //   when...

    int  x, y;
    bool x_reverse, y_reverse;
    int  width, height;

    virtual bool is_background () { return false; }
    void add_flag (int index) { flags.push_back(index); }
    void add_string (const char *str) { strings.push_back(str); }
    bool flag_stopped();
    bool flag_playing();
    bool flag_disconnected();
    bool flag_justify(Justify *just);

    virtual const char *label()=0;

    virtual void check_size (CairoUI *ui, struct dimensions *dim) {}
    virtual void activate (CairoUI *ui, struct dimensions *dim) {}
    virtual bool check_activate(CairoUI *ui);
    virtual bool left_gravity (Layout *lay) { return false; }
    virtual bool top_gravity (Layout *lay) { return false; }

    void write_line(LayoutProcessor *proc, const char *name);
    virtual void write_line (LayoutProcessor *proc) {}

  protected:
    bool flag_check(int index1, int index2=-1, int *value=0);
    vector<int> flags;
    vector<string> strings;
    };

class LayoutPos : public Position
    {
  public:
    LayoutPos (const char *name)
        { layout_name = strdup(name); }
    ~LayoutPos ()
        { free(layout_name); }

    char *layout_name;
    const char *label () { return layout_name; }
    void write_line(LayoutProcessor *proc);
    void activate(CairoUI *ui, struct dimensions *dim);
    };

class WidgetPos : public Position
    {
  public:
    WidgetPos (WidgetIndex index)
        : widget_index(index) {}

    const char *label();
    Widget *widget;
    WidgetIndex widget_index;

    void write_line(LayoutProcessor *proc);
    bool check_activate(CairoUI *ui);
    void check_size(CairoUI *ui, struct dimensions *dim);
    void activate(CairoUI *ui, struct dimensions *dim);
    bool left_gravity(Layout *lay);
    bool top_gravity(Layout *lay);
    bool is_background () { return !widget->is_physical(); }
    };

enum SpacerIndex
    {
    SPACER_SQUARE,
    //SPACER_RECTANGLE,
    //SPACER_LINE,
    SPACER_MAX = SPACER_SQUARE
    };

class SpacerPos : public Position
    {
  public:
    SpacerPos (SpacerIndex index) : type(index) {}

    const char *label();
    SpacerIndex type;
    void write_line(LayoutProcessor *proc);
    void check_size(CairoUI *ui, struct dimensions *dim);
    };

class Gravity
    {
  public:
    WidgetIndex widget;
//    char *layout_name;
    int x, y;

    bool left();
    bool right();
    bool above();
    bool below();
    };

class RatioLayout
    {
  public:
    RatioLayout (double x_ratio, double y_ratio)
        : ratio_x(x_ratio), ratio_y(y_ratio)
        {}
    double ratio_x,
           ratio_y;
    list<Position *> pos;

    void write(LayoutProcessor *proc);
    };

class Layout
    {
  public:
    Layout (const char *layout_label)
        : label(0), selectable(false), allocates(false),
          base_x(0), base_y(0)
        {
        if ( layout_label && *layout_label )
            label = strdup(layout_label);
        gravity.clear();  // unneeded?
        }
    ~Layout ()
        { if ( label ) free(label); }

    char *label;
    bool selectable;  // informational flag (means: user selectable)
    bool allocates;   // each Position element uses some of the available size
    int base_x, base_y;
    vector<Gravity> gravity;

    // - contains either the pos list OR a ratio_list
    list<Position *> pos;            // -   Positions that define the layout
    list<RatioLayout *> ratio_list;  // OR: layouts for different ratios
    };

static list<Layout *> layout;
static Layout *selected;

// ---------------------------------------------------------------------- <fold<
//    Config file - declarations                                          >fold>

static struct assoc_array
    cfg_pos_x[] =
        {   {"=", INHERIT},  {"left of", LEFT_OF},  {"right of", RIGHT_OF},
            {"center to right", CENTER_TO_RIGHT},
            {"center to left", CENTER_TO_LEFT},
            {"center of", CENTER_OF},  {"center", CENTER},  {0, -1000}
        },
    cfg_pos_y[] =
        {   {"=", INHERIT},  {"above", ABOVE},  {"below", BELOW},
            {"center above", CENTER_ABOVE},  {"center below", CENTER_BELOW},
            {"center of", CENTER_OF},  {"center", CENTER},  {0, -1000}
        },
    cfg_pos_width[] =
        {   {"=", INHERIT},
            {"expand right", EXPAND_RIGHT},  {"expand left", EXPAND_LEFT},
            {"expand", EXPAND},  {"*", FULL},  {0, -1000}
        },
    cfg_pos_height[] =
        {   {"=", INHERIT},
            {"expand up", EXPAND_UP},  {"expand down", EXPAND_DOWN},
            {"expand", EXPAND},  {"*", FULL},  {0, -1000}
        };

static struct assoc_array cfg_widget[WI_MAX+2] =
    {   {"Stop", WI_STOP}, {"Play", WI_PLAY}, {"Pause", WI_PAUSE},
        {"Play/Pause", WI_PLAYPAUSE},
        {"Previous", WI_PREVIOUS}, {"Next", WI_NEXT},
        {"Volume", WI_VOLUME}, {"Position", WI_POSITION},
        {"Time Display", WI_TIMEDISPLAY}, {"Progress Bar", WI_PROGRESSBAR},
        {"Icon", WI_ICON}, {"Cover Art", WI_COVERART},
        {"Test Canvas", WI_TEST_CANVAS},
//        {"Visualization", WI_VISUAL},
    {0, -1000} };

static struct assoc_array cfg_spacer_widget[SPACER_MAX+2] =
    {   {"Square", SPACER_SQUARE},
    {0, -1000} };

const char * WidgetPos::label ()
    {
    return cfg_widget[widget_index].key;
    }

const char * SpacerPos::label ()
    {
    return cfg_spacer_widget[type].key;
    }

// If any of these elements change position, make sure the flag checks
// below are still correct

static struct assoc_array cfg_position_flag[] =
        {   {"(stopped)", 0}, {"(playing)", 0}, {"(disconnected)", 0},
            {"left justify", JUSTIFY_LEFT}, {"right justify", JUSTIFY_RIGHT},
            {"center justify", JUSTIFY_CENTER}, {0, -1000}
        };


class LayoutProcessor : public ConfigProcessor
    {
  public:
    LayoutProcessor (CairoUI *obj)
        {
        label = "UI.CAIRO LAYOUT";
        ui = obj;
        selected_name = 0;
        }
    ~LayoutProcessor ()
        { if ( selected_name ) free(selected_name); }

    void init_read();
    void data_read(const char *key, char *value);
    void read_done();
    void write_data();

    CairoUI *ui;
    char *selected_name;

    bool read_position_line(Position *obj, char *value);
    void read_gravity_field(char *p);
    };

// ---------------------------------------------------------------------- <fold<
//    -             utility                                               >fold>

bool Position::flag_check (int index1, int index2, int *value)
    {
    if ( index2 == -1 ) index2 = index1;
    for ( vector<int>::iterator it = flags.begin(); it != flags.end(); it++ )
        if ( *it >= index1 && *it <= index2 )
            {
            if ( value )
                *value = cfg_position_flag[*it].value;
            return true;
            }
    return false;
    }

bool Position::flag_stopped () { return flag_check(0); }
bool Position::flag_playing () { return flag_check(1); }
bool Position::flag_disconnected () { return flag_check(2); }

bool Position::flag_justify (Justify *just)
    {
    int value;
    if ( flag_check(3, 5, &value) )
        {
        *just = (Justify)value;
        return true;
        }
    return false;
    }

bool Gravity::left ()  { return cfg_pos_x[x].value == LEFT_OF; }
bool Gravity::right () { return cfg_pos_x[x].value == RIGHT_OF; }
bool Gravity::above () { return cfg_pos_y[y].value == ABOVE; }
bool Gravity::below () { return cfg_pos_y[y].value == BELOW; }

#if 1
int lays () { return layout.size(); }
Layout *layb () { return layout.back(); }
Layout *layf () { return layout.front(); }

list<Position *> layp (Layout *lay) { return lay->pos; }
int layp_size (Layout *lay) { return lay->pos.size(); }
Position *laypf (Layout *lay) { return lay->pos.front(); }
Position *laypb (Layout *lay) { return lay->pos.back(); }
#endif
static Layout *find_layout (const char *name)
    {
    for ( list<Layout *>::iterator it_L = layout.begin();
          it_L != layout.end();
          it_L++ )
        if ( strcasecmp((*it_L)->label, name) == 0 )
            return *it_L;
    return 0;
    }

bool Position::check_activate (CairoUI *ui) 
    {
    if ( mcp->connected() )
        {
        if ( flag_disconnected() )
            return false;
        if ( mcp->stopped() )
            return !flag_playing();
        else
            return !flag_stopped();
        }
    else
        return flag_disconnected();
    return true;
    }

/*
static bool layout_has_ratio_list (Layout *lay)
    {
    LayoutPos *lp;
    Layout *first_lay;
    return ( (lp = dynamic_cast<LayoutPos *>(lay->pos.front())) != 0 &&
             (first_lay = find_layout(lp->layout_name)) != 0 &&
             first_lay->ratio_layout );
    }
*/
                
// ---------------------------------------------------------------------- <fold<
//    -             reading                                               >fold>

/*
    void read_position_flag(vector<int> flags, char *p);
void LayoutProcessor::read_position_flag (vector<int> flags, char *p)
    {
    char *next;
    while ( p )
        {
        next = next_token(p, ',', true);
        if ( assoc_match(p, cfg_position_flag, &index, 0) )
            flags.push_back(index);
        p = next;
        }
    }
*/

#if 0
  // TODO  decide if there's any reason to verify these, probably best to
  //       just ignore wrong ones
          it != pos->flags.end();
            {
            switch ( index )
                {
                case 0: case 1: case 2:
                    switch ( obj->type )
                        {
                        case WT_POSITION:
                            ui->w->position.justify = (Justify)value;
                            break;
                        case WT_TIMEDISPLAY:
                            ui->w->timedisplay.justify = (Justify)value;
                            break;
                        default: // FIXME not a great message
                            read_error(p, "flag", "matching position flag");
                        }
                    break;
                }
            }
#endif

void LayoutProcessor::init_read ()
    {
    while ( !layout.empty() )
        {
        delete layout.front();
        layout.pop_front();
        }
    layout.clear();
    if ( selected_name )
        free(selected_name);
    selected_name = 0;
    }

void LayoutProcessor::read_done ()
    {
    if ( selected_name )
        {
        selected = find_layout(selected_name);
        free(selected_name);
        selected_name = 0;
        }
    }

void LayoutProcessor::data_read (const char *key, char *value)
    {
    if ( (strcasecmp(key, "layout") == 0) )
        {
        layout.push_back(new Layout(value));
        return;
        }
    else if ( strcasecmp(key, "selected") == 0 )
        {
        if ( selected_name ) free(selected_name);
        selected_name = strdup(value);
        return;
        }

    if ( layout.empty() )
        {
        read_error(key, value, "a layout label before other elements");
        return;
        }

    Layout *lay = layout.back();

    //  TODO  fire a warning if any of these are inside ratio layouts
    if ( strcasecmp(key, "base size") == 0 )
        {
        if ( sscanf(value, "%d x %d", &lay->base_x, &lay->base_y) == 2 )
            ;
        else
            read_error(key, value, "<width>x<height>, e.g. 80x50");
        }
    else if ( strcasecmp(key, "selectable") == 0 )
        lay->selectable = !is_false_string(value);
    else if ( strcasecmp(key, "gravity") == 0 )
        read_gravity_field(value);
    else if ( strcasecmp(key, "layout mode") == 0 )
        {
        if ( strcasecmp(value, "allocates") == 0 )
            lay->allocates = true;
        else
            if ( *value != '\0' )
                read_error(key, value, "allocates (only mode currently)");
        }
    else if ( strcasecmp(key, "ratio") == 0 )
        {
        double rx, ry;
        if ( sscanf(value, "%lf / %lf", &rx, &ry) == 2 )
            lay->ratio_list.push_back(new RatioLayout(rx, ry));
        else
            read_error(key, value, "<width>/<height>, e.g. 2/1.5");
        }
    else
        {
        RatioLayout *ratlay = 0;
        int index;
        Position *obj = 0;

        if ( !lay->ratio_list.empty() )
            ratlay = lay->ratio_list.back();

        if ( assoc_match(key, cfg_widget, 0, &index) )
            {
            WidgetPos *o = new WidgetPos((WidgetIndex)index);
            obj = o;
            }
        else if ( assoc_match(key, cfg_spacer_widget, 0, &index) )
            {
            SpacerPos *o = new SpacerPos((SpacerIndex)index);
            obj = o;
            }
        else
            {
            LayoutPos *o = new LayoutPos(key);
            obj = o;
            }
        if ( read_position_line(obj, value) )
            {
            if ( ratlay )
                ratlay->pos.push_back(obj);
            else
                lay->pos.push_back(obj);
            }
        else
            {
            delete obj;
            read_error(key, value, "position specification");
            }
        }
    }

//   Stop: 1,1, 16,16, flag, flag...
bool LayoutProcessor::read_position_line (Position *obj, char *value_str)
    {
    char *p, *next;
    int value;
/*
    Position blank;
    Position *previous;
    if ( !layout.back()->pos.empty() )
        previous = layout.back()->pos.back();
    else
        {
        blank.x = blank.y = blank.width = blank.height = 0;
        previous = &blank;
        }
*/

    p = skip_spaces(value_str);
    next = next_token(p, ',', true);

    if ( sscanf(p, "-%d", &value) == 1 )
        {  // this must be separate for the (quite likely) case of -0
        obj->x_reverse = true;
        obj->x = value;
        }
    else if ( sscanf(p, "%d", &value) == 1 ||
              assoc_match(p, cfg_pos_x, 0, &value) )
        obj->x = value;
    if ( !next ) return false;

    p = next;
    next = next_token(p, ',', true);
    if ( sscanf(p, "-%d", &value) == 1 )
        {
        obj->y_reverse = true;
        obj->y = value;
        }
    else if ( sscanf(p, "%d", &value) == 1 ||
              assoc_match(p, cfg_pos_y, 0, &value) )
        obj->y = value;

    if ( !next )
        {
        obj->width = FULL;
        obj->height = FULL;
        return true;
        }

    p = next;
    next = next_token(p, ',', true);
    if ( sscanf(p, "%d", &value) == 1 ||
         assoc_match(p, cfg_pos_width, 0, &value) )
        obj->width = value;
    if ( !next ) return false;

    p = next;
    next = next_token(p, ',', true);
    if ( sscanf(p, "%d", &value) == 1 ||
         assoc_match(p, cfg_pos_height, 0, &value) )
        obj->height = value;

    while ( next )
        {
        int index;
        p = next;
        next = next_token(p, ',', true);
        if ( assoc_match(p, cfg_position_flag, &index, 0) )
            obj->add_flag(index);
        else
            obj->add_string(p);
        }
    return true;
    }

void LayoutProcessor::read_gravity_field (char *p)
    {
    Gravity grav;
    char *next;
    int index;
    next = next_token(p, ',', true);
    if ( assoc_match(p, cfg_widget, 0, &index) )
        {
        grav.widget = (WidgetIndex)index;
        p = next;
        next = next_token(p, ',', true);
        if ( p && next && assoc_match(p, cfg_pos_x, &grav.x, 0) &&
             assoc_match(next, cfg_pos_y, &grav.y, 0) )
            {
            layout.back()->gravity.push_back(grav);
            }
        else
            read_error("Gravity", "", "<left of|right of>, <above|below>");
        }
    else
        read_error("Gravity", p, "the name of a widget type");
    }

// ---------------------------------------------------------------------- <fold<
//    -             writing                                               >fold>

void LayoutProcessor::write_data ()
    {
    const int PAD = 9;
//    list<WidgetPos *>::iterator it_W;
    list<Layout *>::iterator it_L;

    cfgfile->indent = 4;
    if ( selected )
        cfgfile->write("Selected", PAD, true, selected->label);

    for ( it_L = layout.begin(); it_L != layout.end(); it_L++ )
        {
        Layout *lay = *it_L;
        if ( lay != layout.front() )
            cfgfile->write("\n");

        cfgfile->indent = 4;
        cfgfile->write("Layout", PAD, true, lay->label);
        cfgfile->indent = 8;

        if ( lay->selectable )
            cfgfile->write("Selectable", PAD, true, 0);

        if ( lay->base_x != 0 && lay->base_y != 0 )
            cfgfile->write("Base size", PAD, true, "%dx%d",
                      lay->base_x, lay->base_y);

        if ( lay->allocates )
            cfgfile->write("Layout mode", PAD, true, "allocates");

        vector<Gravity>::iterator itg;
        for ( itg = lay->gravity.begin(); itg != lay->gravity.end(); itg++ )
            {
            cfgfile->write("Gravity", PAD, true, "%s, %s, %s",
                           cfg_widget[(*itg).widget].key,
                           cfg_pos_x[(*itg).x].key, cfg_pos_y[(*itg).y].key);
            }

        for ( list<Position *>::iterator it = lay->pos.begin();
              it != lay->pos.end();
              it++ )
            (*it)->write_line(this);

        for ( list<RatioLayout *>::iterator it = lay->ratio_list.begin();
              it != lay->ratio_list.end();
              it++ )
            {
            // TODO  don't print .00000 for values close to a whole number
            cfgfile->write("Ratio", PAD, true, "%lf / %lf", 
                           (*it)->ratio_x, (*it)->ratio_y);
            cfgfile->indent += 4;
            (*it)->write(this);
            cfgfile->indent -= 4;
            }
        }
    }

void RatioLayout::write (LayoutProcessor *proc)
    {
    list<Position *>::iterator it_p;
    for ( it_p = pos.begin(); it_p != pos.end(); it_p++ )
        (*it_p)->write_line(proc);
    }

void Position::write_line (LayoutProcessor *proc, const char *name)
    {
    char buf[255];
    char *p = buf;
    const char *key;

    if ( x >= 0 )
        {
        if ( x_reverse )
            *(p++) = '-';
        p += sprintf(p, "%d", x);
        }
    else
        { // ASSERT this
        assoc_by_value(x, cfg_pos_x, 0, &key);
        p += sprintf(p, key);
        }
    *(p++) = ',';

    if ( y >= 0 )
        {
        if ( y_reverse )
            *(p++) = '-';
        p += sprintf(p, "%d", y);
        }
    else
        {
        assoc_by_value(y, cfg_pos_y, 0, &key);
        p += sprintf(p, key);
        }
    *(p++) = ','; *(p++) = ' ';

    if ( width >= 0 )
        p += sprintf(p, "%d", width);
    else
        {
        assoc_by_value(width, cfg_pos_width, 0, &key);
        p += sprintf(p, key);
        }
    *(p++) = ',';

    if ( height >= 0 )
        p += sprintf(p, "%d", height);
    else
        {
        assoc_by_value(height, cfg_pos_height, 0, &key);
        p += sprintf(p, key);
        }

    for ( vector<int>::iterator it = flags.begin(); it != flags.end(); it++ )
        p += sprintf(p, ", %s", cfg_position_flag[*it].key);

    for ( vector<string>::iterator it = strings.begin();
          it != strings.end(); it++ )
        p += sprintf(p, ", %s", (*it).c_str());

    *(p++) = '\0';
    proc->cfgfile->write(name, 13, true, buf);
    }

void LayoutPos::write_line (LayoutProcessor *proc)
    {
    Position::write_line(proc, layout_name);
    }

void WidgetPos::write_line (LayoutProcessor *proc)
    {
    Position::write_line(proc, cfg_widget[widget_index].key);
    }

void SpacerPos::write_line (LayoutProcessor *proc)
    {
    const char *name;
    assoc_by_value(type, cfg_spacer_widget, 0, &name);
    Position::write_line(proc, name);
    }

// ---------------------------------------------------------------------- <fold<
//    Widget Layout 'rithmetic                                            >fold>

// For now, just choosing the first selectable layout in the list.
static void select_layout ()
    {
    selected = layout.front();  // default to the first, if none are selectable
    if ( selected->selectable )
        return;

    list<Layout *>::iterator it_L;
    for ( it_L = layout.begin(); it_L != layout.end(); it_L++ )
        {
        Layout *tmp = *it_L;
        if ( tmp->selectable )
            selected = tmp;
        }
    }

static RatioLayout * choose_ratio_layout (Layout *lay, double ratio)
    {
    RatioLayout *result = 0;
    double result_ratio = 0;
    double check_ratio;

    for ( list<RatioLayout *>::iterator
          it = lay->ratio_list.begin(); it != lay->ratio_list.end(); it++ )
        {
        check_ratio = (double)((*it)->ratio_x) / (double)((*it)->ratio_y);
        if ( ratio == check_ratio )
            return *it;
        if ( result_ratio == 0 )
            {
            result_ratio = check_ratio;
            result = *it;
            }
        else
            {
            if ( (check_ratio > result_ratio && ratio > check_ratio) ||
                 (check_ratio < result_ratio && ratio < check_ratio) )
                {
                result = *it;
                result_ratio = check_ratio;
                }
            }
        }
    return result;
    }

// Returns an adjusted size.
// Example: Say a widget is 20 units wide in a layout with a base width of 50.
//          If applied to an area that is 100 pixels wide (actual_size) the
//          width of the widget is 40.  (100 * 20 / 50)
static int adj (double units, double actual_size, int base_size)
    { return base_size >= 0 ? actual_size * units / base_size : 0; }

/*
// See if one pair of y's collide with another.
static bool y_collides (double Ay1, double Ay2, double By1, double By2)
    {
    return !(Ay1 > By2 && Ay2 < By1);
    }
*/

// return the farthest x1 can increase without hitting a widget
static int go_right (CairoUI *ui, double x1, double y1, double y2, double limit)
    {
    for ( Widget *o = ui->widget_list; o; o = o->next )
        if ( o->is_physical() )
            {
            // is the widget right of the starting x and left of our limit?
            if ( o->x1 > x1 && o->x1 < limit &&
                 // does the widget collide with the y1y2 line?
                 !(o->y1 > y2 || o->y2 < y1) )
                limit = o->x1;  // we hit this widget, new limit
            }
    return limit;
    }

static int go_left (CairoUI *ui, double x1, double y1, double y2, double limit)
    {
    for ( Widget *o = ui->widget_list; o; o = o->next )
        if ( o->is_physical() )
            {
            if ( o->x2 < x1 && o->x2 > limit &&
                 !(o->y1 > y2 || o->y2 < y1) )
                limit = o->x2;
            }
    return limit;
    }

static int go_up (CairoUI *ui, double y1, double x1, double x2, double limit)
    {
    for ( Widget *o = ui->widget_list; o; o = o->next )
        if ( o->is_physical() )
            {
            if ( o->y2 < y1 && o->y2 > limit &&
                 !(o->x1 > x2 || o->x2 < x1) )
                limit = o->y2;
            }
    return limit;
    }

static int go_down (CairoUI *ui, double y1, double x1, double x2, double limit)
    {
    for ( Widget *o = ui->widget_list; o; o = o->next )
        if ( o->is_physical() )
            {
            if ( o->y1 > y1 && o->y1 < limit &&
                 !(o->x1 > x2 || o->x2 < x1) )
                limit = o->y1;
            }
    return limit;
    }

bool WidgetPos::left_gravity (Layout *lay)
    {
    for ( unsigned int i = 0; i < lay->gravity.size(); i++ )
        if ( lay->gravity[i].widget == widget_index )
            return lay->gravity[i].left();
    return Position::left_gravity(lay);
    }

bool WidgetPos::top_gravity (Layout *lay)
    {
    for ( unsigned int i = 0; i < lay->gravity.size(); i++ )
        if ( lay->gravity[i].widget == widget->layout_index )
            return lay->gravity[i].above();
    return Position::top_gravity(lay);
    }

void WidgetPos::check_size (CairoUI *ui, struct dimensions *dim)
    {
    double tmpw = widget->width, tmph = widget->height;
    widget->width = dim->width;  widget->height = dim->height;
    widget->check_size(ui);
    dim->width = widget->width;  dim->height = widget->height;
    // undo the widget size for now
    widget->width = tmpw; widget->height = tmph;
    }

void SpacerPos::check_size (CairoUI *ui, struct dimensions *dim)
    {
    switch ( type )
        {
        case SPACER_SQUARE:
            if ( dim->height > dim->width ) dim->height = dim->width;
            if ( dim->width > dim->height ) dim->width = dim->height;
            break;
        }
    }

/* An allocation layout divides the available area between Position elements.
 * Each element is given a rectangle on one side of the area and that portion
 * is removed.  The last element typically uses the remaining space.
 *
 * Simply put, this is what is used to put the cover art on one side.
 *
 * Currently the specifiers are very limited:
 * -  x,y  must be 0,0 (all); 1,0 (horizontal); or 0,1 (vertical)
 * -  width and height may be FULL, a size, or = (inherited).
 *    -  Note: If two elements are to be the same width or height, the second
 *             should use inherit ('=') as the available space will change
 *             after the first is allocated.
 *    -  Setting both the width and the height to a size currently fails.
 */
// Returns false if area->width or area->height are 0.
// The widget size should not be applied if pdim->width or height are set to 0.
static bool alloc_position (CairoUI *ui, Layout *lay,
                            struct dimensions *area, Position *pos,
                            struct dimensions *pdim, struct dimensions *last)
    {
    if ( area->width <= 0 || area->height <= 0 )
        return false;
    *pdim = *area;
    if ( pos->x == 1 )  // placed on a side
        {
//        if ( pos->width == FULL )
        if ( pos->width >= 0 )  // just treat any non-number as FULL, for now
            pdim->width = adj(pos->width, area->width, lay->base_x);
        pos->check_size(ui, pdim);
        // move to the right?  (default)
        if ( pdim->width < area->width && !pos->left_gravity(lay) )
            pdim->x1 = area->x2 - pdim->width;
        }
    else if ( pos->y == 1 )  // placed on top/bottom
        {
        if ( pos->height >= 0 )
            pdim->height = adj(pos->height, area->height, lay->base_y);
        pos->check_size(ui, pdim);
        // move to the bottom?   (default)
        if ( pdim->height < area->height && !pos->top_gravity(lay) )
            pdim->y1 = area->y2 - pdim->height;
        }
    else  // already set to the area
        {
        pos->check_size(ui, pdim);
        }
    pdim->x2 = pdim->x1 + pdim->width;
    pdim->y2 = pdim->y1 + pdim->height;

    // Remove the position's final dimensions from the available area
    if ( pdim->width > 0 && pdim->height > 0 )
        {
        if ( pdim->width == area->width && pdim->height == area->height )
            {
            area->width = 0;
            area->height = 0;
            }
        else if ( pdim->width == area->width )  // remove vertical space
            {
            if ( pdim->y1 == area->y1 )
                area->y1 = pdim->y2;
            else
                area->y2 = pdim->y1;
            area->height = area->y2 - area->y1;
            }
        else if ( pdim->height == area->height )  // remove horizontal space
            {
            if ( pdim->x1 == area->x1 )
                area->x1 = pdim->x2;
            else
                area->x2 = pdim->x1;
            area->width = area->x2 - area->x1;
            }
        else   // FIXME Make a guess as to what rectangle to remove
            assert(1);  // widget/layout does not fit a whole side
        }

    return true;
    }

enum Need { DIM_X1 = 0, DIM_Y1,
            DIM_X2,     DIM_Y2,
            DIM_WIDTH,  DIM_HEIGHT };

static bool process_position (CairoUI *ui, Layout *lay,
                              struct dimensions *area, Position *pos,
                              struct dimensions *pdim, struct dimensions *last)
    {
    #define HAVE(dim) FLAG(have, dim)
    #define NEEDS_MET(needvar) ((needvar) != 0 && \
                                ((needvar) & have) == (needvar))
    int pos_x_need = 0,      pos_y_need = 0,
        pos_width_need = 0,  pos_height_need = 0,
        size_check = 0,
        have = 0,
        middle_x_need = 0,   middle_y_need = 0;
    double middle_x, middle_y;
    int passes;

    SET_FLAG(size_check, DIM_WIDTH);
    SET_FLAG(size_check, DIM_HEIGHT);

/// Setup for pos->x
    if ( pos->x >= 0 )
        {
        if ( !pos->x_reverse )
            {
            pdim->x1 = area->x1;
            if ( pos->x > 0 )
                pdim->x1 += adj(pos->x, area->width, lay->base_x);
            SET_FLAG(have, DIM_X1);
            }
        else
            {
            pdim->x2 = area->x2 - adj(pos->x, area->width, lay->base_x);
            SET_FLAG(have, DIM_X2);
            }
        }
    else if ( pos->x == INHERIT )
        {
        pdim->x1 = last->x1;
        SET_FLAG(have, DIM_X1);
        }
    else if ( pos->x == LEFT_OF )
        {
        pdim->x2 = last->x1;
        SET_FLAG(have, DIM_X2);
        }
    else if ( pos->x == RIGHT_OF )
        {
        pdim->x1 = last->x2;
        SET_FLAG(have, DIM_X1);
        }
    else if ( pos->x == CENTER_OF )
        {
        middle_x = (last->x1 + last->x2) / 2;
        SET_FLAG(middle_x_need, DIM_WIDTH);
        }
    else if ( pos->x == CENTER )
        {
        middle_x = (area->x1 + area->x2) / 2;
        SET_FLAG(middle_x_need, DIM_WIDTH);
        }
    else if ( pos->x == CENTER_TO_LEFT || pos->x == CENTER_TO_RIGHT )
        {
        if ( pos->x == CENTER_TO_LEFT )
            { pdim->x2 = last->x1; SET_FLAG(have, DIM_X2); }
        else
            { pdim->x1 = last->x2; SET_FLAG(have, DIM_X1); }
        SET_FLAG(pos_x_need, DIM_WIDTH);
        SET_FLAG(pos_x_need, DIM_HEIGHT);
        }
/// Setup for pos->y
    if ( pos->y >= 0 )
        {
        if ( !pos->y_reverse )
            {
            pdim->y1 = area->y1;
            if ( pos->y > 0 )
                pdim->y1 += adj(pos->y, area->height, lay->base_y);
            SET_FLAG(have, DIM_Y1);
            }
        else
            {
            pdim->y2 = area->y2 - adj(pos->y, area->height, lay->base_y);
            SET_FLAG(have, DIM_Y2);
            }
        }
    else if ( pos->y == INHERIT )
        {
        pdim->y1 = last->y1;
        SET_FLAG(have, DIM_Y1);
        }
    else if ( pos->y == ABOVE )
        {
        pdim->y2 = last->y1;
        SET_FLAG(have, DIM_Y2);
        }
    else if ( pos->y == BELOW )
        {
        pdim->y1 = last->y2;
        SET_FLAG(have, DIM_Y1);
        }
    else if ( pos->y == CENTER_OF )
        {
        middle_y = (last->y1 + last->y2) / 2;
        SET_FLAG(middle_y_need, DIM_HEIGHT);
        }
    else if ( pos->y == CENTER )
        {
        middle_y = (area->y1 + area->y2) / 2;
        SET_FLAG(middle_y_need, DIM_HEIGHT);
        }
    else if ( pos->y == CENTER_ABOVE || pos->y == CENTER_BELOW )
        {
        if ( pos->y == CENTER_ABOVE )
            { pdim->y2 = last->y1; SET_FLAG(have, DIM_Y2); }
        else
            { pdim->y1 = last->y2; SET_FLAG(have, DIM_Y1); }
        SET_FLAG(pos_y_need, DIM_WIDTH);
        SET_FLAG(pos_y_need, DIM_HEIGHT);
        }
/// Setup for pos->width
    if ( pos->width == EXPAND ||
         pos->width == EXPAND_RIGHT || pos->width == EXPAND_LEFT )
        {
        SET_FLAG(pos_width_need, DIM_Y1);
        SET_FLAG(pos_width_need, DIM_Y2);
        if ( pos->width == EXPAND_LEFT )
            SET_FLAG(pos_width_need, DIM_X2);
        else if ( pos->width == EXPAND_RIGHT )
            SET_FLAG(pos_width_need, DIM_X1);
        }
    else
        {
        if ( pos->width == INHERIT )
            pdim->width = last->width;
        else if ( pos->width == 0 )
            pos->width = 0;
        else if ( pos->width > 0 )
            pdim->width = adj(pos->width, area->width, lay->base_x);
        else if ( pos->width == FULL )
            {
            pdim->x1 = area->x1;  SET_FLAG(have, DIM_X1);
            pdim->x2 = area->x2;  SET_FLAG(have, DIM_X2);
            pdim->width = area->width;
            }
        SET_FLAG(have, DIM_WIDTH);
        }
/// Setup for pos->height
    if ( pos->height == EXPAND ||
         pos->height == EXPAND_UP || pos->height == EXPAND_DOWN )
        {
        SET_FLAG(pos_height_need, DIM_X1);
        SET_FLAG(pos_height_need, DIM_X2);
        if ( pos->height == EXPAND_UP )
            SET_FLAG(pos_height_need, DIM_Y2);
        else if ( pos->height == EXPAND_DOWN )
            SET_FLAG(pos_height_need, DIM_Y1);
        }
    else
        {
        if ( pos->height == INHERIT )
            pdim->height = last->height;
        else if ( pos->height == 0 )
            pos->height = 0;
        else if ( pos->height > 0 )
            pdim->height = adj(pos->height, area->height, lay->base_y);
        else if ( pos->height == FULL )
            {
            pdim->y1 = area->y1;  SET_FLAG(have, DIM_Y1);
            pdim->y2 = area->y2;  SET_FLAG(have, DIM_Y2);
            pdim->height = area->height;
            }
        SET_FLAG(have, DIM_HEIGHT);
        }

/// This loop is NEEDY! ///
    passes = 1;
    for ( ; passes < 50; passes++ )
        {
        if ( NEEDS_MET(size_check) )
            {
            pos->check_size(ui, pdim);
            if ( HAVE(DIM_X1) && HAVE(DIM_X2) &&
                 (pdim->x2 - pdim->x1 > pdim->width) )
                {
                if ( pos->x == EXPAND_LEFT || pos->x_reverse ||
                     pos->x == CENTER_TO_LEFT || pos->x == LEFT_OF )
                    pdim->x1 = pdim->x2 - pdim->width;
                else if ( pos->x == CENTER )
                    center_points(&pdim->x1, &pdim->x2, pdim->width);
                else
                    pdim->x2 = pdim->x1 + pdim->width;
                }
            if ( HAVE(DIM_Y1) && HAVE(DIM_Y2) &&
                 (pdim->y2 - pdim->y1 > pdim->height) )
                {
                if ( pos->y == EXPAND_UP || pos->y_reverse ||
                     pos->y == CENTER_ABOVE || pos->y == ABOVE )
                    pdim->y1 = pdim->y2 - pdim->height;
                else if ( pos->y == CENTER )
                    center_points(&pdim->y1, &pdim->y2, pdim->height);
                else
                    pdim->y2 = pdim->y1 + pdim->height;
                }
            size_check = 0;
            }

        if ( !HAVE(DIM_X1) && HAVE(DIM_X2) && HAVE(DIM_WIDTH) )
            {
            pdim->x1 = pdim->x2 - pdim->width;
            SET_FLAG(have, DIM_X1);
            }
        else if ( !HAVE(DIM_X2) && HAVE(DIM_X1) && HAVE(DIM_WIDTH) )
            {
            pdim->x2 = pdim->x1 + pdim->width;
            SET_FLAG(have, DIM_X2);
            }
        if ( !HAVE(DIM_Y1) && HAVE(DIM_Y2) && HAVE(DIM_HEIGHT) )
            {
            pdim->y1 = pdim->y2 - pdim->height;
            SET_FLAG(have, DIM_Y1);
            }
        else if ( !HAVE(DIM_Y2) && HAVE(DIM_Y1) && HAVE(DIM_HEIGHT) )
            {
            pdim->y2 = pdim->y1 + pdim->height;
            SET_FLAG(have, DIM_Y2);
            }

        if ( NEEDS_MET(middle_x_need) )
            {
            pdim->x1 = middle_x;
            pdim->x2 = middle_x;
            center_points(&pdim->x1, &pdim->x2, pdim->width);
            /* TODO  Decide whether to shift a widget over or make it smaller
             *       in the case of an overrun.
             */
            double overrun;
            if ( (overrun = (area->x1 - pdim->x1)) > 0 )
                {
                pdim->x1 += overrun;
                pdim->x2 += overrun;
                }
            else if ( (overrun = (pdim->x2 - area->x2)) > 0 )
                {
                pdim->x1 -= overrun;
                pdim->x2 -= overrun;
                }
            SET_FLAG(have, DIM_X1);
            SET_FLAG(have, DIM_X2);
            middle_x_need = 0;
            continue;
            }
        if ( NEEDS_MET(middle_y_need) )
            {
            pdim->y1 = middle_y;
            pdim->y2 = middle_y;
            center_points(&pdim->y1, &pdim->y2, pdim->height);
            double overrun;
            if ( (overrun = (area->y1 - pdim->y1)) > 0 )
                {
                pdim->y1 += overrun;
                pdim->y2 += overrun;
                }
            else if ( (overrun = (pdim->y2 - area->y2)) > 0 )
                {
                pdim->y1 -= overrun;
                pdim->y2 -= overrun;
                }
            SET_FLAG(have, DIM_Y1);
            SET_FLAG(have, DIM_Y2);
            middle_y_need = 0;
            continue;
            }

        if ( NEEDS_MET(pos_x_need) )
            {
            //if ( pos->x_reverse )  handled at the front of the loop
            if ( pos->x == CENTER_TO_LEFT )
                {
                pdim->x1 = go_left(ui, last->x1, pdim->y1, pdim->y2, area->x1);
                pdim->x2 = last->x1;
                center_points(&pdim->x1, &pdim->x2, pdim->width);
                SET_FLAG(have, DIM_X1);
                SET_FLAG(have, DIM_X2);
                }
            else if ( pos->x == CENTER_TO_RIGHT )
                {
                pdim->x1 = last->x2;
                pdim->x2 = go_right(ui, last->x2, pdim->y1, pdim->y2, area->x2);
                center_points(&pdim->x1, &pdim->x2, pdim->width);
                SET_FLAG(have, DIM_X1);
                SET_FLAG(have, DIM_X2);
                }
            else if ( pos->x == CENTER )
                {
                // x1 and x2 are set by middle_x
                }
            else assert(2 + 2 == 5);
            pos_x_need = 0;
            continue;
            }

        if ( NEEDS_MET(pos_y_need) )
            {
            //if ( pos->y_reverse )  handled at the front of the loop
            if ( pos->y == CENTER_ABOVE )
                {
                pdim->y1 = go_up(ui, last->y1, pdim->x1, pdim->x2, area->y1);
                pdim->y2 = last->y1;
                center_points(&pdim->y1, &pdim->y2, pdim->height);
                SET_FLAG(have, DIM_Y1);
                SET_FLAG(have, DIM_Y2);
                }
            else if ( pos->y == CENTER_BELOW )
                {
                pdim->y1 = last->y2;
                pdim->y2 = go_down(ui, last->y2, pdim->x1, pdim->x2, area->y2);
                center_points(&pdim->y1, &pdim->y2, pdim->height);
                SET_FLAG(have, DIM_Y1);
                SET_FLAG(have, DIM_Y2);
                }
            else assert(2 + 2 == 5);
            pos_y_need = 0;
            continue;
            }

        if ( NEEDS_MET(pos_width_need) )
            {
            if ( pos->width == EXPAND_RIGHT )
                {
                pdim->x2 = go_right(ui, pdim->x1, pdim->y1, pdim->y2, area->x2);
                SET_FLAG(have, DIM_X2);
                }
            else if ( pos->width == EXPAND_LEFT )
                {
                pdim->x1 = go_left(ui, pdim->x2, pdim->y1, pdim->y2, area->x1);
                SET_FLAG(have, DIM_X1);
                }
            else if ( pos->width == EXPAND )
                {
                if ( HAVE(DIM_X1) )
                    pdim->x2 = pdim->x1;
                else if ( HAVE(DIM_X2) )
                    pdim->x1 = pdim->x2;
                else if ( pos->x == CENTER )
                    {
                    pdim->x1 = pdim->x2 = (area->x1 + area->x2) / 2;
                    middle_x_need = 0;
                    }
                else assert(2 + 2 == 5);
                pdim->x1 = go_left(ui, pdim->x1, pdim->y1, pdim->y2, area->x1);
                pdim->x2 = go_right(ui, pdim->x2, pdim->y1, pdim->y2, area->x2);
                SET_FLAG(have, DIM_X1);
                SET_FLAG(have, DIM_X2);
                }
            else assert(2 + 2 == 5);
            pdim->width = pdim->x2 - pdim->x1;
            SET_FLAG(have, DIM_WIDTH);

            pos_width_need = 0;
            continue;
            }

        if ( NEEDS_MET(pos_height_need) )
            {
            if ( pos->height == EXPAND_UP )
                {
                pdim->y1 = go_up(ui, pdim->y2, pdim->x1, pdim->x2, area->y1);
                SET_FLAG(have, DIM_Y1);
                }
            else if ( pos->height == EXPAND_DOWN )
                {
                pdim->y2 = go_down(ui, pdim->y1, pdim->x1, pdim->x2, area->y2);
                SET_FLAG(have, DIM_Y2);
                }
            else if ( pos->height == EXPAND )
                {
                if ( HAVE(DIM_Y1) )
                    pdim->y2 = pdim->y1;
                else if ( HAVE(DIM_Y2) )
                    pdim->y1 = pdim->y2;
                else if ( pos->y == CENTER )
                    {
                    middle_y_need = 0;
                    pdim->y1 = pdim->y2 = (area->y1 + area->y2) / 2;
                    }
                else assert(2 + 2 == 5);
                pdim->y1 = go_up(ui, pdim->y1, pdim->x1, pdim->x2, area->y1);
                pdim->y2 = go_down(ui, pdim->y2, pdim->x1, pdim->x2, area->y2);
                SET_FLAG(have, DIM_Y1);
                SET_FLAG(have, DIM_Y2);
                }
            else assert(2 + 2 == 5);
            pdim->height = pdim->y2 - pdim->y1;
            SET_FLAG(have, DIM_HEIGHT);

            pos_height_need = 0;
            continue;
            }

        if ( pos_x_need == 0 && pos_y_need == 0 &&
             pos_width_need == 0 && pos_height_need == 0 &&
             middle_x_need == 0 &&  middle_y_need == 0 &&
             size_check == 0 )
            {
            mcp->log_verbose(11, "%d pass%s for %s (%.1f,%.1f %.1f,%.1f)\n",
                             passes, passes == 1 ? "" : "es", pos->label(),
                             pdim->x1,pdim->y1, pdim->x2,pdim->y2);
            break;
            }
        }

    if ( passes >= 50 )
        {
        mcp->log_verbose(1, "Layout %s contains unusable positioning for %s.\n",
                         lay->label, pos->label());
        // TODO improve this output
        //      -  ideally sanitize the input so this is truly an exception
        mcp->log_verbose(5, "    need: [%2x: %2x,%2x %2x,%2x | %2x,%2x | %2x]\n", have, pos_x_need, pos_y_need, pos_width_need, pos_height_need, middle_x_need, middle_y_need, size_check);
        return false;
        }

    return true;
    #undef HAVE
    #undef NEEDS_MET
    }

/* Error checking is not performed here for the most part.  The layout data
 * and positions specified should be sanitized at the input stage.
 * -  Two reasons: 1) it would be hard to do here,
 *                 2) this can be called quite frequently and must be fast.
 */
static void process_layout (CairoUI *ui, Layout *lay, struct dimensions area)
    {
    struct dimensions last;
    struct dimensions dim;
    Position *pos;

    if ( !lay->ratio_list.empty() )  // handle a ratio-chosen list
        {
        RatioLayout *ratlay = choose_ratio_layout(lay, (double)area.width /
                                                       (double)area.height);
        for ( list<Position *>::iterator
              it = ratlay->pos.begin(); it != ratlay->pos.end(); it++ )
            {
            pos = *it;
            if ( pos->check_activate(ui) )
                {
                if ( lay->allocates && !pos->is_background() )
                    {
                    if ( !alloc_position(ui, lay, &area, pos, &dim, &last) )
                        break;
                    }
                else
                    {
                    process_position(ui, lay, &area, pos, &dim, &last);
                    }

                if ( dim.width > 0 && dim.height > 0 )
                    {
                    pos->activate(ui, &dim);
                    last = dim;
                    }
                }
            }
        }
    else  // handle the Position list
        {
        for ( list<Position *>::iterator
              it = lay->pos.begin(); it != lay->pos.end(); it++ )
            {
            pos = *it;
            if ( pos->check_activate(ui) )
                {
                process_position(ui, lay, &area, pos, &dim, &last);
                if ( dim.width > 0 && dim.height > 0 )
                    {
                    pos->activate(ui, &dim);
                    last = dim;
                    }
                }
            }

        }
    }

void LayoutPos::activate (CairoUI *ui, struct dimensions *dim)
    {
    Layout *lay;
    if ( (lay = find_layout(layout_name)) != 0 )
        process_layout(ui, lay, *dim);
    }

Widget * new_widget_by_index(WidgetIndex index);  // uic_widget.cpp

bool WidgetPos::check_activate (CairoUI *ui)
    {
    if ( !Position::check_activate(ui) )
        return false;

    widget = 0;
    for ( widget = ui->widget_pool; widget; widget = widget->next )
        if ( widget->layout_index == widget_index )
            break;
    if ( !widget )
        {
        widget = new_widget_by_index(widget_index);;
        widget->layout_index = widget_index;
        ui->add_widget_to_list(&ui->widget_pool, widget);
        }
    return true;
    }

void WidgetPos::activate (CairoUI *ui, struct dimensions *dim)
    {
    ui->remove_widget_from_list(&ui->widget_pool, widget);
    ui->add_widget_to_list(&ui->widget_list, widget);

    widget->layout_instance = 0;
    for ( Widget *o = ui->widget_list;o; o = o->next )
        if ( o->layout_index == widget_index )
            widget->layout_instance++;
    if ( widget->layout_instance > 1 )
        mcp->log_verbose(11, "    instance #%d\n", widget->layout_instance);

    widget->x1 = dim->x1;  widget->y1 = dim->y1;
    widget->x2 = dim->x2;  widget->y2 = dim->y2;
    widget->width = dim->width;  widget->height = dim->height;

    Justify just;
    if ( flag_justify(&just) )
        widget->set_justify(just);

    for ( unsigned int i = 0; i < strings.size(); i++ )
        widget->set_option(strings[i].c_str());

    widget->configure(ui);
    }

// TODO  probably delete the no_resize business, better handled by
//       proper double buffering etc.
bool layout_widgets (CairoUI *ui, bool no_resize)
    {
    Widget *o;
    struct dimensions area;
    area.x1 = 0;
    area.y1 = 0;
    area.x2 = ui->width;
    area.y2 = ui->height;
    area.width = ui->width;
    area.height = ui->height;

    if ( !selected ) select_layout();
    if ( !selected ) return false;

/*
    int old_count = 0;
    for (blah)
        old_count++;
    Widget *was_enabled[old_count];
    for (blah)
        old_widget[i] = o;
*/

    if ( ui->widget_list )
        {
        if ( ui->widget_pool )
            {
            ui->widget_pool->prev = ui->widget_list->tail();
            ui->widget_pool->prev->next = ui->widget_pool;
            }
        ui->widget_pool = ui->widget_list;
        ui->widget_list = 0;
        }

    process_layout(ui, selected, area);

    for ( o = ui->widget_list; o; o = o->next )
        o->redraw_needed = true;
/*
    if ( no_resize )
        {
        for ( o = ui->widget_list; o; o = o->next )
            if ( !same_location(o, old_widget, old_count)
                redraw_old_location_which_we_don't_have
        for ( o = ui->widget_pool; o; o = o->next )
            if ( in_old_list(o, old_widget, old_count) )
                o->draw_bg(ui);
        }
*/

/*
    // Sanity-check that at least one widget was enabled
    // ... when CairoUI actually has some recourse on failure
    for ( int i = 0; i <= WT_MAX; i++ )
        if ( ui->widget[i] )
            return true;
    return false;
*/
    return true;
    }

// ---------------------------------------------------------------------- <fold<

ConfigProcessor *uic_layout_processor (CairoUI *ui)
    { return new LayoutProcessor(ui); }

} // namespace uic
